//
// Created by gogoqiu on 2022/6/5.
//

//https://blog.csdn.net/itcastcpp/article/details/7943302

#include "../common/initsock.h"

#include <windows.h>
#include <stdio.h>
#include <Iphlpapi.h>

#include "protoutils.h"
#include "ProtoPacket.h"


#pragma comment(lib, "Iphlpapi.lib")


DWORD WINAPI SendThread(LPVOID lpParam);
BOOL GetGlobalData();

/
// 全局数据
u_char	g_ucLocalMac[6];	// 本地MAC地址
DWORD	g_dwGatewayIP;		// 网关IP地址
DWORD	g_dwLocalIP;		// 本地IP地址
DWORD	g_dwMask;			// 子网掩码

CInitSock theSock;

int main()
{
    // 获取全局数据
    GetGlobalData();
    // 启动服务
    if(!ProtoStartService())
    {
        printf(" ProtoStartService() failed %d \n", ::GetLastError());
        return -1;
    }
    // 打开控制设备对象
    HANDLE hControlDevice = ProtoOpenControlDevice();
    if(hControlDevice == INVALID_HANDLE_VALUE)
    {
        printf(" ProtoOpenControlDevice() failed() %d \n", ::GetLastError());
        ProtoStopService();
        return -1;
    }
    // 枚举绑定的下层适配器
    CPROTOAdapters adapters;
    if(!adapters.EnumAdapters(hControlDevice))
    {
        printf(" Enume adapter failed \n");
        ProtoStopService();
        return -1;
    }

    CAdapter adapter;
    // 默认使用第一个适配器
    if(!adapter.OpenAdapter(adapters.m_pwszSymbolicLink[0], TRUE))
    {
        printf(" OpenAdapter failed \n");
        ProtoStopService();
        return -1;
    }

    CArpPacket arp(&adapter);


    // 下面开始进行LAN扫描

    // 提供当前线程（接收数据的线程）的优先级，避免丢失到来的数据贞
    ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
    // 创建发送线程，开始发送ARP请求
    ::CloseHandle(::CreateThread(NULL, 0, SendThread, &arp, 0, NULL));

    // 接收ARP应答
    u_char *p;
    PARPHeader pArph;
    printf(" 开始扫描LAN中的计算机... \n");
    while(TRUE)
    {
        pArph = arp.WaitReply();
        if(pArph != NULL)
        {

            printf(" \n ------------------------------------------- \n");

            p = pArph->smac;
            printf("	Mac地址:	%02X-%02X-%02X-%02X-%02X-%02X \n", p[0], p[1], p[2], p[3], p[4], p[5]);
            p = (u_char*)&pArph->saddr;
            printf("	 IP地址:	%d.%d.%d.%d  \n ", p[0], p[1], p[2], p[3]);
            //		HOSTENT *pHost = ::gethostbyaddr((char*)&pArph->saddr, 4, AF_INET);
            //		if(pHost != NULL)
            //			printf("	 主机名:	%s \n", pHost->h_name);
        }
        else
        {
            break;
        }
    }
    printf("\n\n 扫描完毕 \n");

    ProtoStopService();

    getchar();

    return 0;
}


DWORD WINAPI SendThread(LPVOID lpParam)
{
CArpPacket *pArp = (CArpPacket *)lpParam;
// 取得LAN中IP地址空间的大小
DWORD dwMask = ::ntohl(g_dwMask);
int nMaxHosts = ~dwMask;

// 设置以太头中目标MAC地址。发送ARP请求时，应该将它设为广播地址
u_char destmacEther[6];
memset(destmacEther, 0xff, 6);
// ARP头中的目标MAC地址
u_char destmacArp[6] = { 0 };
// 向地址空间中的每个IP地址发送ARP请求
DWORD dwTemp = ::ntohl(g_dwGatewayIP & g_dwMask) + 1;
for(int i=1; i<nMaxHosts; i++, dwTemp++)
{
DWORD dwIP = ::htonl(dwTemp);
if(dwIP != g_dwLocalIP)
{
if(!pArp->SendPacket(destmacEther, g_ucLocalMac, ARPOP_REQUEST,
        destmacArp, dwIP, g_ucLocalMac, g_dwLocalIP))
{
printf(" SendPacket() failed \n");
break;
}
}
}
return 0;
}

BOOL GetGlobalData()
{
    PIP_ADAPTER_INFO pAdapterInfo = NULL;
    ULONG ulLen = 0;

    // 为适配器结构申请内存
    ::GetAdaptersInfo(pAdapterInfo,&ulLen);
    pAdapterInfo = (PIP_ADAPTER_INFO)::GlobalAlloc(GPTR, ulLen);

    // 取得本地适配器结构信息
    if(::GetAdaptersInfo(pAdapterInfo,&ulLen) ==  ERROR_SUCCESS)
    {
        if(pAdapterInfo != NULL)
        {
            memcpy(g_ucLocalMac, pAdapterInfo->Address, 6);
            g_dwGatewayIP = ::inet_addr(pAdapterInfo->GatewayList.IpAddress.String);
            g_dwLocalIP = ::inet_addr(pAdapterInfo->IpAddressList.IpAddress.String);
            g_dwMask = ::inet_addr(pAdapterInfo->IpAddressList.IpMask.String);
        }
    }
    ::GlobalFree(pAdapterInfo);

    printf(" \n -------------------- 本地主机信息 -----------------------\n\n");
    in_addr in;
    in.S_un.S_addr = g_dwLocalIP;
    printf("      IP Address : %s \n", ::inet_ntoa(in));

    in.S_un.S_addr = g_dwMask;
    printf("     Subnet Mask : %s \n", ::inet_ntoa(in));

    in.S_un.S_addr = g_dwGatewayIP;
    printf(" Default Gateway : %s \n", ::inet_ntoa(in));

    u_char *p = g_ucLocalMac;
    printf("     MAC Address : %02X-%02X-%02X-%02X-%02X-%02X \n", p[0], p[1], p[2], p[3], p[4], p[5]);

    printf(" \n \n ");



    return TRUE;
}

//https://blog.csdn.net/moooxin/article/details/7327487
void info(){
    //获得网卡流量信息
    DWORD dwIfBufSize = 0;
    BOOL bRet;

    if(m_pMIT == NULL)
        m_pMIT = new MIB_IFTABLE[sizeof(MIB_IFTABLE)];

    bRet = ::GetIfTable(m_pMIT,&dwIfBufSize,0);
    if(bRet == ERROR_INSUFFICIENT_BUFFER)//如果内存不足，则重新分配内存大小
    {
        if(dwIfBufSize != NULL)
            delete [] m_pMIT;
        m_pMIT = new MIB_IFTABLE[dwIfBufSize];
    }

    bRet = ::GetIfTable(m_pMIT,&dwIfBufSize,0);
    if (bRet == NO_ERROR)
    {
        if (m_pMIT->dwNumEntries <= 1)
        {
            //bResult = false;
        }
        else
        {
            __int64 i64TotalOutOctets = 0;
            __int64 i64TotalInOctets = 0;
            //多網卡
            for(int i=0; i<(m_pMIT->dwNumEntries); i++)
            {
                if (m_pMIT->table[i].dwType <= 23)
                {
                    //当前上传
                    i64TotalOutOctets += m_pMIT->table[i].dwOutOctets;
                    //当前下载
                    i64TotalInOctets += m_pMIT->table[i].dwInOctets;
                    //bResult = mit.table[i].dwOperStatus;
                    //if (bResult)
                    {
                        //return true;
                    }
                }
            }
            memset(szVal,0,sizeof(szVal));
            if(m_oldUploadOctets != 0 && (i64TotalOutOctets > m_oldUploadOctets))
                m_curUploadOctets = i64TotalOutOctets - m_oldUploadOctets;
            m_oldUploadOctets = i64TotalOutOctets;
            lengthToStr(m_curUploadOctets * 8,szVal);
            m_CapabilityDlgObj.GetDlgItem(IDC_STA_NETUPSPEED)->SetWindowText(szVal);
            //下载
            memset(szVal,0,sizeof(szVal));
            if(m_oldDownloadOctets != 0 && (i64TotalInOctets > m_oldDownloadOctets))
                m_curDownloadOctets = i64TotalInOctets - m_oldDownloadOctets;
            m_oldDownloadOctets = i64TotalInOctets;
            lengthToStr(m_curDownloadOctets * 8,szVal);
            m_CapabilityDlgObj.GetDlgItem(IDC_STA_NETDNSPEED)->SetWindowText(szVal);
        }
    }
}